import colorsys
import math
from enum import IntEnum

import cv2
import numpy as np
import numpy.linalg as npla

from face_detect.core import imagelib
from face_detect.core import mathlib
from face_detect.core.mathlib.umeyama import umeyama
from face_detect.FaceType import FaceType

mesh_33=[70,63,105,66,107,336,296,334,293,300,168,197,5,4,240,99,2,328,460,33,160,158,133,153,144,362,385,387,263,373,380,57,287]
landmarks_2D_4=np.array([
[0.224152 , 0.2119465], #left iris  mean 37 38 40 41
[0.75610125, 0.2119465],#right iris mean 43 44 46 47
[0.490127, 0.515625], # nose 30
[0.4901265, 0.780233]  #mouth mean 48 54
])
landmarks_2D_4_bottom=np.array([
[0.2218305, 0.244588 ], #left iris  mean 40 41
[0.7584225, 0.244588],#right iris mean 46 47
[0.490127, 0.515625], # nose 30
[0.4901265, 0.780233]  #mouth mean 48 54
])
landmarks_2D = np.array([
    [0.000213256, 0.106454],  # 17
    [0.0752622, 0.038915],  # 18
    [0.18113, 0.0187482],  # 19
    [0.29077, 0.0344891],  # 20
    [0.393397, 0.0773906],  # 21
    [0.586856, 0.0773906],  # 22
    [0.689483, 0.0344891],  # 23
    [0.799124, 0.0187482],  # 24
    [0.904991, 0.038915],  # 25
    [0.98004, 0.106454],  # 26
    [0.490127, 0.203352],  # 27
    [0.490127, 0.307009],  # 28
    [0.490127, 0.409805],  # 29
    [0.490127, 0.515625],  # 30
    [0.36688, 0.587326],  # 31
    [0.426036, 0.609345],  # 32
    [0.490127, 0.628106],  # 33
    [0.554217, 0.609345],  # 34
    [0.613373, 0.587326],  # 35
    [0.121737, 0.216423],  # 36
    [0.187122, 0.178758],  # 37
    [0.265825, 0.179852],  # 38
    [0.334606, 0.231733],  # 39
    [0.260918, 0.245099],  # 40
    [0.182743, 0.244077],  # 41
    [0.645647, 0.231733],  # 42
    [0.714428, 0.179852],  # 43
    [0.793132, 0.178758],  # 44
    [0.858516, 0.216423],  # 45
    [0.79751, 0.244077],  # 46
    [0.719335, 0.245099],  # 47
    [0.254149, 0.780233],  # 48
    [0.340985, 0.745405],  # 49
    [0.428858, 0.727388],  # 50
    [0.490127, 0.742578],  # 51
    [0.551395, 0.727388],  # 52
    [0.639268, 0.745405],  # 53
    [0.726104, 0.780233],  # 54
    [0.642159, 0.864805],  # 55
    [0.556721, 0.902192],  # 56
    [0.490127, 0.909281],  # 57
    [0.423532, 0.902192],  # 58
    [0.338094, 0.864805],  # 59
    [0.290379, 0.784792],  # 60
    [0.428096, 0.778746],  # 61
    [0.490127, 0.785343],  # 62
    [0.552157, 0.778746],  # 63
    [0.689874, 0.784792],  # 64
    [0.553364, 0.824182],  # 65
    [0.490127, 0.831803],  # 66
    [0.42689, 0.824182]  # 67
], dtype=np.float32)

landmarks_2D_new = np.array([
    [0.000213256, 0.106454],  # 17
    [0.0752622, 0.038915],  # 18
    [0.18113, 0.0187482],  # 19
    [0.29077, 0.0344891],  # 20
    [0.393397, 0.0773906],  # 21
    [0.586856, 0.0773906],  # 22
    [0.689483, 0.0344891],  # 23
    [0.799124, 0.0187482],  # 24
    [0.904991, 0.038915],  # 25
    [0.98004, 0.106454],  # 26
    [0.490127, 0.203352],  # 27
    [0.490127, 0.307009],  # 28
    [0.490127, 0.409805],  # 29
    [0.490127, 0.515625],  # 30
    [0.36688, 0.587326],  # 31
    [0.426036, 0.609345],  # 32
    [0.490127, 0.628106],  # 33
    [0.554217, 0.609345],  # 34
    [0.613373, 0.587326],  # 35
    [0.121737, 0.216423],  # 36
    [0.187122, 0.178758],  # 37
    [0.265825, 0.179852],  # 38
    [0.334606, 0.231733],  # 39
    [0.260918, 0.245099],  # 40
    [0.182743, 0.244077],  # 41
    [0.645647, 0.231733],  # 42
    [0.714428, 0.179852],  # 43
    [0.793132, 0.178758],  # 44
    [0.858516, 0.216423],  # 45
    [0.79751, 0.244077],  # 46
    [0.719335, 0.245099],  # 47
    [0.254149, 0.780233],  # 48
    [0.726104, 0.780233],  # 54
], dtype=np.float32)
landmarks_2D_new_mesh = np.array([
[ 0.000213256,  0.106454  ], #17
[ 0.0752622,    0.038915  ], #18
[0.1281961, 0.0288316], #19[ 0.18113,      0.0187482 ]
[ 0.29077,      0.0144891 ], #20
[ 0.393397,     0.0773906 ], #21
[ 0.586856,     0.0773906 ], #22
[ 0.689483,     0.0144891 ], #23
[0.8520575, 0.0288316], #24[ 0.799124,     0.0187482 ]
[ 0.904991,     0.038915  ], #25
[ 0.98004,      0.106454  ], #26
[ 0.490127,     0.203352  ], #27
[ 0.490127,     0.307009  ], #28
[ 0.490127,     0.409805  ], #29
[ 0.490127,     0.515625  ], #30
[0.396458 , 0.5983355], #31 [ 0.36688,      0.587326  ]
[ 0.426036,     0.609345  ], #32
[ 0.490127,     0.628106  ], #33
[ 0.554217,     0.609345  ], #34
[ 0.613373,     0.587326  ], #35
[ 0.071737,     0.136423  ], #36
[ 0.137122,     0.118758  ], #37
[ 0.215825,     0.119852  ], #38
[ 0.334606,     0.151733  ], #39
[ 0.210918,     0.165099  ], #40
[ 0.132743,     0.164077  ], #41
[ 0.645647,     0.151733  ], #42
[ 0.764428,     0.119852  ], #43
[ 0.743132,     0.118758  ], #44
[ 0.908516,     0.136423  ], #45
[ 0.84751,      0.164077  ], #46
[ 0.769335,     0.165099  ], #47
[ 0.254149,     0.780233  ], #48
[ 0.726104,     0.780233  ], #54
], dtype=np.float32)

# landmarks_468_moving_parts_indexes = [0, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 46, 52, 53, 54, 55, 56, 57, 58, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 95, 96, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 117, 118, 124, 130, 132, 133, 135, 136, 138, 139, 140, 143, 144, 145, 146, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 168, 169, 170, 171, 172, 173, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 189, 190, 191, 192, 193, 194, 199, 200, 201, 202, 204, 208, 210, 211, 212, 213, 214, 215, 221, 222, 223, 224, 225, 226, 228, 229, 230, 231, 232, 233, 243, 244, 245, 246, 247, 249, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 267, 268, 269, 270, 271, 272, 273, 276, 282, 283, 284, 285, 286, 287, 288, 291, 292, 293, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 306, 307, 308, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 324, 325, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 346, 347, 353, 359, 361, 362, 364, 365, 367, 368, 369, 372, 373, 374, 375, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 394, 395, 396, 397, 398, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 413, 414, 415, 416, 417, 418, 421, 422, 424, 428, 430, 431, 432, 433, 434, 435, 441, 442, 443, 444, 445, 446, 448, 449, 450, 451, 452, 453, 463, 464, 465, 466, 467]
# uni_landmarks_468 = np.array(
# [[ 0.49066195,  0.7133885 ],
#        [ 0.49042386,  0.52723485],
#        [ 0.49050152,  0.6244965 ],
#        [ 0.45844677,  0.39348277],
#        [ 0.4905825 ,  0.49120593],
#        [ 0.49006602,  0.43998772],
#        [ 0.48907965,  0.26775706],
#        [ 0.11721139,  0.23243594],
#        [ 0.48957095,  0.11063451],
#        [ 0.48949632,  0.03535742],
#        [ 0.48905632, -0.25326234],
#        [ 0.4907858 ,  0.73766613],
#        [ 0.49081355,  0.7606857 ],
#        [ 0.4908666 ,  0.7839426 ],
#        [ 0.49079415,  0.78913504],
#        [ 0.4908271 ,  0.80801845],
#        [ 0.49086872,  0.831855  ],
#        [ 0.49092326,  0.8631041 ],
#        [ 0.49104446,  0.94170016],
#        [ 0.49009967,  0.5546924 ],
#        [ 0.44398275,  0.5741402 ],
#        [-0.2106727 ,  0.00861922],
#        [ 0.2523662 ,  0.2832579 ],
#        [ 0.2042254 ,  0.28945392],
#        [ 0.1552372 ,  0.28322184],
#        [ 0.09056008,  0.24730967],
#        [ 0.30096018,  0.27277085],
#        [ 0.21548809,  0.16713436],
#        [ 0.2595488 ,  0.17071684],
#        [ 0.16957955,  0.17298089],
#        [ 0.13164258,  0.18425746],
#        [ 0.043018  ,  0.28581   ],
#        [ 0.30856833,  1.0507976 ],
#        [ 0.10015843,  0.22331452],
#        [-0.20773543,  0.26701325],
#        [-0.02414621,  0.25144747],
#        [ 0.23481508,  0.5045001 ],
#        [ 0.44063616,  0.7097012 ],
#        [ 0.4449884 ,  0.762481  ],
#        [ 0.3840104 ,  0.7218947 ],
#        [ 0.33943903,  0.73847425],
#        [ 0.40284824,  0.76374006],
#        [ 0.36457124,  0.76704985],
#        [ 0.26937196,  0.84716266],
#        [ 0.46683946,  0.5275276 ],
#        [ 0.4642676 ,  0.49167544],
#        [ 0.06039319,  0.11509081],
#        [ 0.31504983,  0.36394927],
#        [ 0.3660137 ,  0.52945083],
#        [ 0.3509634 ,  0.50311893],
#        [ 0.09496811,  0.5005815 ],
#        [ 0.46075967,  0.4424029 ],
#        [ 0.20108324,  0.05883435],
#        [ 0.12877828,  0.07731954],
#        [-0.09675749, -0.09848522],
#        [ 0.39672711,  0.09345116],
#        [ 0.29908365,  0.18449144],
#        [ 0.23298171,  0.7922538 ],
#        [-0.27583498,  0.85219014],
#        [ 0.38898414,  0.5723152 ],
#        [ 0.41446668,  0.59347576],
#        [ 0.28167963,  0.7884952 ],
#        [ 0.30013445,  0.7875627 ],
#        [ 0.09448256,  0.03961415],
#        [ 0.3531811 ,  0.5553779 ],
#        [ 0.2873921 ,  0.05599196],
#        [ 0.28232294,  0.01076962],
#        [ 0.1903341 , -0.23029903],
#        [ 0.0108011 , -0.03099815],
#        [ 0.24915197, -0.10741784],
#        [ 0.01047484,  0.08868673],
#        [-0.08942058,  0.05201372],
#        [ 0.44268388,  0.7376863 ],
#        [ 0.39652622,  0.741894  ],
#        [ 0.35389552,  0.7514722 ],
#        [ 0.393559  ,  0.5851372 ],
#        [ 0.2925385 ,  0.7871472 ],
#        [ 0.31904542,  0.80939215],
#        [ 0.32005206,  0.787085  ],
#        [ 0.4195982 ,  0.5444628 ],
#        [ 0.3688312 ,  0.78418756],
#        [ 0.40608776,  0.7841225 ],
#        [ 0.4472093 ,  0.78405076],
#        [ 0.43053833,  0.9379409 ],
#        [ 0.44192585,  0.8617842 ],
#        [ 0.44321233,  0.82923037],
#        [ 0.4432334 ,  0.80578357],
#        [ 0.44304678,  0.78921837],
#        [ 0.36314115,  0.7893578 ],
#        [ 0.36057413,  0.8040033 ],
#        [ 0.35472178,  0.8187327 ],
#        [ 0.34614718,  0.83330894],
#        [ 0.2959003 ,  0.69076014],
#        [-0.37090415,  0.5509728 ],
#        [ 0.4903264 ,  0.5851119 ],
#        [ 0.3370172 ,  0.78961957],
#        [ 0.33070365,  0.8010128 ],
#        [ 0.43397966,  0.6231119 ],
#        [ 0.35356513,  0.59569615],
#        [ 0.42509514,  0.6093918 ],
#        [ 0.2635329 ,  0.39636588],
#        [ 0.19704658,  0.43663597],
#        [ 0.33384863,  0.52658314],
#        [ 0.03225203, -0.18047164],
#        [ 0.11854403, -0.08533629],
#        [ 0.18350407,  0.01215954],
#        [ 0.31292278,  0.8845064 ],
#        [ 0.3862302 ,  0.02093028],
#        [ 0.36480215, -0.1098879 ],
#        [ 0.33342764, -0.2497105 ],
#        [ 0.11592615,  0.2646692 ],
#        [-0.00803981,  0.3294946 ],
#        [ 0.33535972,  0.26431814],
#        [ 0.05940344,  0.18766014],
#        [ 0.36188984,  0.33336782],
#        [ 0.39879864,  0.50869733],
#        [-0.07952328,  0.36885905],
#        [ 0.04230375,  0.36800843],
#        [ 0.11137532,  0.3864613 ],
#        [ 0.19386435,  0.37397826],
#        [ 0.25749052,  0.34993485],
#        [ 0.310977  ,  0.3240539 ],
#        [ 0.44813582,  0.2762354 ],
#        [-0.06039021,  0.4864401 ],
#        [ 0.00945808,  0.17624807],
#        [ 0.4739895 ,  0.55369264],
#        [ 0.32125092,  0.4170324 ],
#        [-0.36162117,  0.27013144],
#        [ 0.3592803 ,  0.3023075 ],
#        [ 0.30784345,  0.529875  ],
#        [ 0.07601253,  0.22579695],
#        [ 0.3824061 ,  0.47686696],
#        [-0.33810768,  0.70034444],
#        [ 0.34643772,  0.24336138],
#        [ 0.42429656,  0.45338264],
#        [ 0.02854156,  0.939626  ],
#        [-0.04352415,  1.0322431 ],
#        [-0.20510256,  0.51651907],
#        [-0.06969981,  0.8698207 ],
#        [-0.1581445 ,  0.14948419],
#        [ 0.2889787 ,  1.1224228 ],
#        [ 0.47446907,  0.58377683],
#        [ 0.2818322 ,  0.4586393 ],
#        [-0.08708218,  0.2627534 ],
#        [ 0.16877942,  0.25976214],
#        [ 0.21234928,  0.267416  ],
#        [ 0.30676025,  0.81592965],
#        [-0.06259334,  0.6009466 ],
#        [ 0.36930662,  1.2302231 ],
#        [ 0.17070079,  1.149443  ],
#        [ 0.07714309,  1.0989524 ],
#        [ 0.48931465, -0.1052461 ],
#        [ 0.49159575,  1.2484183 ],
#        [ 0.2527582 ,  0.26420003],
#        [ 0.30066028,  0.25829503],
#        [ 0.3310663 ,  0.25034374],
#        [-0.05075949,  0.16421606],
#        [ 0.29250854,  0.19938153],
#        [ 0.2522571 ,  0.18826446],
#        [ 0.21220936,  0.18724632],
#        [ 0.16866222,  0.19260857],
#        [ 0.13789575,  0.2011967 ],
#        [-0.29335994,  0.12383505],
#        [ 0.1379709 ,  0.24424627],
#        [ 0.49057597,  0.65296   ],
#        [ 0.34147182,  0.663431  ],
#        [ 0.3941785 ,  0.5603462 ],
#        [ 0.43007633,  0.6569765 ],
#        [ 0.48963526,  0.17996965],
#        [ 0.11681002,  1.0107123 ],
#        [ 0.19942053,  1.068824  ],
#        [ 0.38605705,  1.1563928 ],
#        [-0.16756529,  0.9615808 ],
#        [ 0.32817602,  0.21989337],
#        [ 0.41141313,  0.3578073 ],
#        [ 0.49127796,  1.1678538 ],
#        [ 0.27080515,  1.195178  ],
#        [-0.19307071,  0.6481067 ],
#        [ 0.399859  ,  0.7892937 ],
#        [ 0.39875022,  0.80587196],
#        [ 0.39717573,  0.8256797 ],
#        [ 0.3931817 ,  0.85224336],
#        [ 0.3670306 ,  0.9161113 ],
#        [ 0.3256227 ,  0.7724022 ],
#        [ 0.31488904,  0.76426226],
#        [ 0.3001029 ,  0.7583232 ],
#        [ 0.2565659 ,  0.73397243],
#        [ 0.0438394 ,  0.6234349 ],
#        [ 0.40628996,  0.30296788],
#        [ 0.37707803,  0.19498621],
#        [ 0.34125936,  0.21069102],
#        [ 0.33733743,  0.7842425 ],
#        [ 0.00882016,  0.769232  ],
#        [ 0.4335431 ,  0.1821002 ],
#        [ 0.33409703,  0.9826546 ],
#        [ 0.49011812,  0.3896104 ],
#        [ 0.45311242,  0.34152514],
#        [ 0.4899982 ,  0.33611432],
#        [ 0.369907  ,  0.43193236],
#        [ 0.49116373,  1.0932964 ],
#        [ 0.49107185,  1.0132186 ],
#        [ 0.41421878,  1.008873  ],
#        [ 0.21551576,  0.8785059 ],
#        [ 0.27587482,  0.57461077],
#        [ 0.2683325 ,  0.9399872 ],
#        [ 0.17091931,  0.56899554],
#        [ 0.23741819,  0.6283017 ],
#        [ 0.12783033,  0.65916985],
#        [ 0.39875996,  1.0855893 ],
#        [ 0.33251646,  0.45881665],
#        [ 0.16138549,  0.93153137],
#        [ 0.23269826,  0.99740875],
#        [ 0.17994387,  0.8051213 ],
#        [-0.06026869,  0.7033027 ],
#        [ 0.10063827,  0.8241594 ],
#        [-0.15810522,  0.7679798 ],
#        [ 0.2014156 ,  0.7000692 ],
#        [ 0.365875  ,  0.3839739 ],
#        [ 0.4115726 ,  0.5293855 ],
#        [ 0.378973  ,  0.5476473 ],
#        [ 0.43235463,  0.49621448],
#        [ 0.3385827 ,  0.15134089],
#        [ 0.27179635,  0.12940899],
#        [ 0.21341887,  0.12485553],
#        [ 0.15807948,  0.12881717],
#        [ 0.10610204,  0.14814937],
#        [ 0.03133116,  0.236169  ],
#        [-0.21341309,  0.38895622],
#        [ 0.07818349,  0.3101151 ],
#        [ 0.1318462 ,  0.32528982],
#        [ 0.19485526,  0.32642388],
#        [ 0.25329807,  0.31256682],
#        [ 0.30569646,  0.29578218],
#        [ 0.34839994,  0.2842457 ],
#        [-0.3824783 ,  0.41054142],
#        [ 0.37162504,  0.5664833 ],
#        [ 0.41687053,  0.40615496],
#        [ 0.4433516 ,  0.5242282 ],
#        [ 0.44805393,  0.5562703 ],
#        [ 0.43453053,  0.5407472 ],
#        [ 0.37351128,  0.58924097],
#        [ 0.46121803,  0.55474806],
#        [ 0.45942986,  0.5810936 ],
#        [ 0.35955238,  0.24802393],
#        [ 0.38181108,  0.25985107],
#        [ 0.40143687,  0.26679716],
#        [ 0.11717269,  0.2102652 ],
#        [ 0.0940459 ,  0.2016577 ],
#        [ 0.5217974 ,  0.39331725],
#        [ 0.8625129 ,  0.23113514],
#        [ 0.5369363 ,  0.57397795],
#        [ 1.1896138 ,  0.00617525],
#        [ 0.7275363 ,  0.28242856],
#        [ 0.7756985 ,  0.2884565 ],
#        [ 0.82466465,  0.28205347],
#        [ 0.88921595,  0.24591576],
#        [ 0.6788919 ,  0.27210945],
#        [ 0.7640089 ,  0.166177  ],
#        [ 0.7199609 ,  0.16991326],
#        [ 0.8099376 ,  0.17186326],
#        [ 0.8479136 ,  0.18300733],
#        [ 0.9368992 ,  0.28424102],
#        [ 0.67367214,  1.0503516 ],
#        [ 0.8795338 ,  0.22195426],
#        [ 1.1875838 ,  0.26458502],
#        [ 1.0039485 ,  0.24965489],
#        [ 0.74551606,  0.50375396],
#        [ 0.54075617,  0.7095265 ],
#        [ 0.5365969 ,  0.76231945],
#        [ 0.59742403,  0.7215222 ],
#        [ 0.6420548 ,  0.7379461 ],
#        [ 0.5787324 ,  0.7634331 ],
#        [ 0.617019  ,  0.766611  ],
#        [ 0.71218634,  0.8469107 ],
#        [ 0.513503  ,  0.52683127],
#        [ 0.5170686 ,  0.49132976],
#        [ 0.91894245,  0.11362247],
#        [ 0.66487545,  0.36299667],
#        [ 0.61502695,  0.52894545],
#        [ 0.6296784 ,  0.50242335],
#        [ 0.88566196,  0.49919614],
#        [ 0.5193738 ,  0.4423927 ],
#        [ 0.7780587 ,  0.05788935],
#        [ 0.8504331 ,  0.07610969],
#        [ 1.0753254 , -0.1005309 ],
#        [ 0.5824533 ,  0.09305263],
#        [ 0.6804744 ,  0.18382579],
#        [ 0.7485537 ,  0.79121745],
#        [ 1.2577202 ,  0.8495136 ],
#        [ 0.59192824,  0.57196105],
#        [ 0.5665197 ,  0.59321034],
#        [ 0.6999867 ,  0.7877651 ],
#        [ 0.6814933 ,  0.7868972 ],
#        [ 0.8846023 ,  0.03829005],
#        [ 0.62761134,  0.5547819 ],
#        [ 0.6917209 ,  0.05532694],
#        [ 0.6966465 ,  0.01012804],
#        [ 0.7876697 , -0.2309872 ],
#        [ 0.9680314 , -0.03263693],
#        [ 0.7294528 , -0.1080169 ],
#        [ 0.96877015,  0.08704082],
#        [ 1.0685298 ,  0.05000517],
#        [ 0.538806  ,  0.7375185 ],
#        [ 0.5849781 ,  0.7415651 ],
#        [ 0.62764204,  0.7509944 ],
#        [ 0.58739805,  0.5847989 ],
#        [ 0.68912315,  0.78645504],
#        [ 0.6626941 ,  0.8087924 ],
#        [ 0.6616096 ,  0.7864889 ],
#        [ 0.5612171 ,  0.5442156 ],
#        [ 0.61282057,  0.7837617 ],
#        [ 0.575564  ,  0.7838267 ],
#        [ 0.5344426 ,  0.7838985 ],
#        [ 0.551505  ,  0.93764293],
#        [ 0.5399973 ,  0.8616131 ],
#        [ 0.53859717,  0.8290639 ],
#        [ 0.5384943 ,  0.8056173 ],
#        [ 0.53862303,  0.78905153],
#        [ 0.6185288 ,  0.78891206],
#        [ 0.62114686,  0.8035485 ],
#        [ 0.62705064,  0.81825733],
#        [ 0.635676  ,  0.8328036 ],
#        [ 0.6854969 ,  0.69067734],
#        [ 1.3517375 ,  0.54796624],
#        [ 0.64465326,  0.78908265],
#        [ 0.6510032 ,  0.8004538 ],
#        [ 0.5471015 ,  0.62291807],
#        [ 0.62742317,  0.59512955],
#        [ 0.55593795,  0.6091671 ],
#        [ 0.7161671 ,  0.39546603],
#        [ 0.7836529 ,  0.435396  ],
#        [ 0.64694774,  0.5258542 ],
#        [ 0.94603044, -0.1820665 ],
#        [ 0.86011904, -0.08652072],
#        [ 0.79549086,  0.01118712],
#        [ 0.66893554,  0.8840338 ],
#        [ 0.59274685,  0.02056277],
#        [ 0.613851  , -0.11025709],
#        [ 0.64526045, -0.25000137],
#        [ 0.8639107 ,  0.26336375],
#        [ 0.9881146 ,  0.3277454 ],
#        [ 0.6445285 ,  0.26371115],
#        [ 0.92017305,  0.18616839],
#        [ 0.61790556,  0.3323734 ],
#        [ 0.58225924,  0.5077285 ],
#        [ 1.0597262 ,  0.36687428],
#        [ 0.93791103,  0.36642405],
#        [ 0.86892897,  0.38505408],
#        [ 0.78624976,  0.37287512],
#        [ 0.7223912 ,  0.34902957],
#        [ 0.6687594 ,  0.32310694],
#        [ 0.5315497 ,  0.2757726 ],
#        [ 1.0409807 ,  0.48452145],
#        [ 0.9700836 ,  0.17458573],
#        [ 0.5065989 ,  0.55419755],
#        [ 0.6590531 ,  0.41624966],
#        [ 1.3414742 ,  0.26715896],
#        [ 0.62023264,  0.30108824],
#        [ 0.67289865,  0.5290446 ],
#        [ 0.9036883 ,  0.22435239],
#        [ 0.59769833,  0.47659585],
#        [ 1.3194624 ,  0.6974514 ],
#        [ 0.63339525,  0.24286939],
#        [ 0.5571053 ,  0.45250946],
#        [ 0.9535533 ,  0.9380257 ],
#        [ 1.0260391 ,  1.0303764 ],
#        [ 1.1858007 ,  0.51410204],
#        [ 1.0515786 ,  0.867869  ],
#        [ 1.1375865 ,  0.14722979],
#        [ 0.6935665 ,  1.1218798 ],
#        [ 0.5063422 ,  0.58382744],
#        [ 0.69926125,  0.45745537],
#        [ 1.0669235 ,  0.26074636],
#        [ 0.8110406 ,  0.25864118],
#        [ 0.7674977 ,  0.26644707],
#        [ 0.67500204,  0.81528693],
#        [ 1.0435516 ,  0.5990178 ],
#        [ 0.6121316 ,  1.2306852 ],
#        [ 0.81222653,  1.1483234 ],
#        [ 0.9056057 ,  1.0975065 ],
#        [ 0.7270778 ,  0.26337218],
#        [ 0.6791554 ,  0.25763443],
#        [ 0.6487802 ,  0.24975733],
#        [ 1.0302606 ,  0.16233999],
#        [ 0.68710136,  0.19869283],
#        [ 0.72731376,  0.18743533],
#        [ 0.7673578 ,  0.1862774 ],
#        [ 0.81092334,  0.1914876 ],
#        [ 0.84171957,  0.1999683 ],
#        [ 1.2727026 ,  0.12110176],
#        [ 0.8417947 ,  0.24301787],
#        [ 0.63978463,  0.6627527 ],
#        [ 0.5866921 ,  0.5600102 ],
#        [ 0.5511283 ,  0.6567636 ],
#        [ 0.8655194 ,  1.009457  ],
#        [ 0.78306264,  1.0678959 ],
#        [ 0.59620714,  1.1564037 ],
#        [ 1.149833  ,  0.9592815 ],
#        [ 0.65151644,  0.21932903],
#        [ 0.56865776,  0.3571483 ],
#        [ 0.71228063,  1.1944076 ],
#        [ 1.1742088 ,  0.6457327 ],
#        [ 0.5818109 ,  0.78897613],
#        [ 0.5829775 ,  0.80555046],
#        [ 0.5846211 ,  0.82535255],
#        [ 0.5887078 ,  0.8519021 ],
#        [ 0.6150045 ,  0.916079  ],
#        [ 0.65597004,  0.771831  ],
#        [ 0.66669285,  0.7636482 ],
#        [ 0.6814582 ,  0.7576576 ],
#        [ 0.7245435 ,  0.73241323],
#        [ 0.9371713 ,  0.62184393],
#        [ 0.5736738 ,  0.30186948],
#        [ 0.60240346,  0.19448838],
#        [ 0.6383993 ,  0.21017241],
#        [ 0.64431435,  0.7837067 ],
#        [ 0.9726586 ,  0.7675604 ],
#        [ 0.54576766,  0.18157108],
#        [ 0.6477745 ,  0.98230904],
#        [ 0.5269076 ,  0.34123868],
#        [ 0.61068684,  0.43131724],
#        [ 0.56792   ,  1.0087004 ],
#        [ 0.7662271 ,  0.8776794 ],
#        [ 0.7048996 ,  0.57387614],
#        [ 0.7136024 ,  0.9394351 ],
#        [ 0.8097781 ,  0.56784695],
#        [ 0.7435453 ,  0.62753886],
#        [ 0.85328954,  0.6578133 ],
#        [ 0.5835228 ,  1.0854707 ],
#        [ 0.64810187,  0.45811343],
#        [ 0.82059515,  0.9304676 ],
#        [ 0.7494546 ,  0.9966611 ],
#        [ 0.8015866 ,  0.80400985],
#        [ 1.0415541 ,  0.70138854],
#        [ 0.8809724 ,  0.8228132 ],
#        [ 1.1396528 ,  0.7657218 ],
#        [ 0.7798614 ,  0.69881856],
#        [ 0.6143189 ,  0.383193  ],
#        [ 0.56934875,  0.52867246],
#        [ 0.60162777,  0.54706186],
#        [ 0.5470082 ,  0.4963955 ],
#        [ 0.6408297 ,  0.15073723],
#        [ 0.7075675 ,  0.12865019],
#        [ 0.76593757,  0.12391254],
#        [ 0.8212976 ,  0.12768434],
#        [ 0.87334216,  0.14682971],
#        [ 0.948411  ,  0.23457018],
#        [ 1.1936799 ,  0.38651106],
#        [ 0.90181875,  0.30865455],
#        [ 0.84818983,  0.3240165 ],
#        [ 0.7851249 ,  0.32537246],
#        [ 0.72658616,  0.3116911 ],
#        [ 0.6740513 ,  0.2949461 ],
#        [ 0.63111407,  0.28325075],
#        [ 1.362823  ,  0.4074953 ],
#        [ 0.60951644,  0.5658945 ],
#        [ 0.5634702 ,  0.4055624 ],
#        [ 0.5374476 ,  0.5247268 ],
#        [ 0.53280455,  0.5561224 ],
#        [ 0.5462737 ,  0.5405522 ],
#        [ 0.6075077 ,  0.58877414],
#        [ 0.51933056,  0.55477065],
#        [ 0.52143395,  0.58103496],
#        [ 0.62030756,  0.24758299],
#        [ 0.59746987,  0.2574137 ],
#        [ 0.5780933 ,  0.2652785 ],
#        [ 0.8624742 ,  0.2089644 ],
#        [ 0.8855709 ,  0.20027623]], dtype=np.float32)

# mesh_33 = np.arange(468)
# mask = np.ones(len(mesh_33), dtype=bool)
# mask[landmarks_468_moving_parts_indexes]=False
# mesh_33=mesh_33[mask,...]
# landmarks_2D_new_mesh=uni_landmarks_468[mask,...]
# mouth_center_landmarks_2D = np.array([
#     [-4.4202591e-07, 4.4916576e-01],  # 48
#     [1.8399176e-01, 3.7537053e-01],  # 49
#     [3.7018123e-01, 3.3719531e-01],  # 50
#     [5.0000089e-01, 3.6938059e-01],  # 51
#     [6.2981832e-01, 3.3719531e-01],  # 52
#     [8.1600773e-01, 3.7537053e-01],  # 53
#     [1.0000000e+00, 4.4916576e-01],  # 54
#     [8.2213330e-01, 6.2836081e-01],  # 55
#     [6.4110327e-01, 7.0757812e-01],  # 56
#     [5.0000089e-01, 7.2259867e-01],  # 57
#     [3.5889623e-01, 7.0757812e-01],  # 58
#     [1.7786618e-01, 6.2836081e-01],  # 59
#     [7.6765373e-02, 4.5882553e-01],  # 60
#     [3.6856663e-01, 4.4601500e-01],  # 61
#     [5.0000089e-01, 4.5999300e-01],  # 62
#     [6.3143289e-01, 4.4601500e-01],  # 63
#     [9.2323411e-01, 4.5882553e-01],  # 64
#     [6.3399029e-01, 5.4228687e-01],  # 65
#     [5.0000089e-01, 5.5843467e-01],  # 66
#     [3.6601129e-01, 5.4228687e-01]  # 67
# ], dtype=np.float32)

# 68 point landmark definitions
landmarks_68_pt = {"mouth": (48, 68),
                   "right_eyebrow": (17, 22),
                   "left_eyebrow": (22, 27),
                   "right_eye": (36, 42),
                   "left_eye": (42, 48),
                   "nose": (27, 36),  # missed one point
                   "jaw": (0, 17)}

landmarks_68_3D = np.array([
    [-73.393523, -29.801432, 47.667532],  # 00
    [-72.775014, -10.949766, 45.909403],  # 01
    [-70.533638, 7.929818, 44.842580],  # 02
    [-66.850058, 26.074280, 43.141114],  # 03
    [-59.790187, 42.564390, 38.635298],  # 04
    [-48.368973, 56.481080, 30.750622],  # 05
    [-34.121101, 67.246992, 18.456453],  # 06
    [-17.875411, 75.056892, 3.609035],  # 07
    [0.098749, 77.061286, -0.881698],  # 08
    [17.477031, 74.758448, 5.181201],  # 09
    [32.648966, 66.929021, 19.176563],  # 10
    [46.372358, 56.311389, 30.770570],  # 11
    [57.343480, 42.419126, 37.628629],  # 12
    [64.388482, 25.455880, 40.886309],  # 13
    [68.212038, 6.990805, 42.281449],  # 14
    [70.486405, -11.666193, 44.142567],  # 15
    [71.375822, -30.365191, 47.140426],  # 16
    [-61.119406, -49.361602, 14.254422],  # 17
    [-51.287588, -58.769795, 7.268147],  # 18
    [-37.804800, -61.996155, 0.442051],  # 19
    [-24.022754, -61.033399, -6.606501],  # 20
    [-11.635713, -56.686759, -11.967398],  # 21
    [12.056636, -57.391033, -12.051204],  # 22
    [25.106256, -61.902186, -7.315098],  # 23
    [38.338588, -62.777713, -1.022953],  # 24
    [51.191007, -59.302347, 5.349435],  # 25
    [60.053851, -50.190255, 11.615746],  # 26
    [0.653940, -42.193790, -13.380835],  # 27
    [0.804809, -30.993721, -21.150853],  # 28
    [0.992204, -19.944596, -29.284036],  # 29
    [1.226783, -8.414541, -36.948060],  # 00
    [-14.772472, 2.598255, -20.132003],  # 01
    [-7.180239, 4.751589, -23.536684],  # 02
    [0.555920, 6.562900, -25.944448],  # 03
    [8.272499, 4.661005, -23.695741],  # 04
    [15.214351, 2.643046, -20.858157],  # 05
    [-46.047290, -37.471411, 7.037989],  # 06
    [-37.674688, -42.730510, 3.021217],  # 07
    [-27.883856, -42.711517, 1.353629],  # 08
    [-19.648268, -36.754742, -0.111088],  # 09
    [-28.272965, -35.134493, -0.147273],  # 10
    [-38.082418, -34.919043, 1.476612],  # 11
    [19.265868, -37.032306, -0.665746],  # 12
    [27.894191, -43.342445, 0.247660],  # 13
    [37.437529, -43.110822, 1.696435],  # 14
    [45.170805, -38.086515, 4.894163],  # 15
    [38.196454, -35.532024, 0.282961],  # 16
    [28.764989, -35.484289, -1.172675],  # 17
    [-28.916267, 28.612716, -2.240310],  # 18
    [-17.533194, 22.172187, -15.934335],  # 19
    [-6.684590, 19.029051, -22.611355],  # 20
    [0.381001, 20.721118, -23.748437],  # 21
    [8.375443, 19.035460, -22.721995],  # 22
    [18.876618, 22.394109, -15.610679],  # 23
    [28.794412, 28.079924, -3.217393],  # 24
    [19.057574, 36.298248, -14.987997],  # 25
    [8.956375, 39.634575, -22.554245],  # 26
    [0.381549, 40.395647, -23.591626],  # 27
    [-7.428895, 39.836405, -22.406106],  # 28
    [-18.160634, 36.677899, -15.121907],  # 29
    [-24.377490, 28.677771, -4.785684],  # 30
    [-6.897633, 25.475976, -20.893742],  # 31
    [0.340663, 26.014269, -22.220479],  # 32
    [8.444722, 25.326198, -21.025520],  # 33
    [24.474473, 28.323008, -5.712776],  # 34
    [8.449166, 30.596216, -20.671489],  # 35
    [0.205322, 31.408738, -21.903670],  # 36
    [-7.198266, 30.844876, -20.328022]  # 37
], dtype=np.float32)

FaceType_to_padding_remove_align = {
    FaceType.HALF: (0.0, False),
    FaceType.MID_FULL: (0.0675, False),
    FaceType.FULL: (0.2109375, False),
    FaceType.FULL_NO_ALIGN: (0.2109375, True),
    FaceType.WHOLE_FACE: (0.40, False),
    FaceType.HEAD: (0.70, False),
    FaceType.HEAD_NO_ALIGN: (0.70, True),
}


def convert_98_to_68(lmrks):
    # jaw
    result = [lmrks[0]]
    for i in range(2, 16, 2):
        result += [(lmrks[i] + (lmrks[i - 1] + lmrks[i + 1]) / 2) / 2]
    result += [lmrks[16]]
    for i in range(18, 32, 2):
        result += [(lmrks[i] + (lmrks[i - 1] + lmrks[i + 1]) / 2) / 2]
    result += [lmrks[32]]

    # eyebrows averaging
    result += [lmrks[33],
               (lmrks[34] + lmrks[41]) / 2,
               (lmrks[35] + lmrks[40]) / 2,
               (lmrks[36] + lmrks[39]) / 2,
               (lmrks[37] + lmrks[38]) / 2,
               ]

    result += [(lmrks[42] + lmrks[50]) / 2,
               (lmrks[43] + lmrks[49]) / 2,
               (lmrks[44] + lmrks[48]) / 2,
               (lmrks[45] + lmrks[47]) / 2,
               lmrks[46]
               ]

    # nose
    result += list(lmrks[51:60])

    # left eye (from our view)
    result += [lmrks[60],
               lmrks[61],
               lmrks[63],
               lmrks[64],
               lmrks[65],
               lmrks[67]]

    # right eye
    result += [lmrks[68],
               lmrks[69],
               lmrks[71],
               lmrks[72],
               lmrks[73],
               lmrks[75]]

    # mouth
    result += list(lmrks[76:96])

    return np.concatenate(result).reshape((68, 2))


def transform_points(points, mat, invert=False):
    if invert:
        mat = cv2.invertAffineTransform(mat)
    points = np.expand_dims(points, axis=1)
    points = cv2.transform(points, mat, points.shape)
    points = np.squeeze(points)
    return points


def get_transform_mat(image_landmarks, output_size, face_type, scale=1.0):
    if not isinstance(image_landmarks, np.ndarray):
        image_landmarks = np.array(image_landmarks)

    # estimate landmarks transform from global space to local aligned space with bounds [0..1]
    mat = umeyama(np.concatenate([image_landmarks[17:49], image_landmarks[54:55]]), landmarks_2D_new, True)[0:2]

    # get corner points in global space
    g_p = transform_points(np.float32([(0, 0), (1, 0), (1, 1), (0, 1), (0.5, 0.5)]), mat, True)
    g_c = g_p[4]

    # calc diagonal vectors between corners in global space
    tb_diag_vec = (g_p[2] - g_p[0]).astype(np.float32)
    tb_diag_vec /= npla.norm(tb_diag_vec)
    bt_diag_vec = (g_p[1] - g_p[3]).astype(np.float32)
    bt_diag_vec /= npla.norm(bt_diag_vec)

    # calc modifier of diagonal vectors for scale and padding value
    # print(face_type)
    padding, remove_align = FaceType_to_padding_remove_align.get(face_type, 0.0)
    mod = (1.0 / scale) * (npla.norm(g_p[0] - g_p[2]) * (padding * np.sqrt(2.0) + 0.5))

    if face_type == FaceType.WHOLE_FACE:
        # adjust vertical offset for WHOLE_FACE, 7% below in order to cover more forehead
        vec = (g_p[0] - g_p[3]).astype(np.float32)
        vec_len = npla.norm(vec)
        vec /= vec_len
        g_c += vec * vec_len * 0.07


    # calc 3 points in global space to estimate 2d affine transform
    if not remove_align:
        l_t = np.array([g_c - tb_diag_vec * mod,
                        g_c + bt_diag_vec * mod,
                        g_c + tb_diag_vec * mod])
    else:
        # remove_align - face will be centered in the frame but not aligned
        l_t = np.array([g_c - tb_diag_vec * mod,
                        g_c + bt_diag_vec * mod,
                        g_c + tb_diag_vec * mod,
                        g_c - bt_diag_vec * mod,
                        ])

        # get area of face square in global space
        area = mathlib.polygon_area(l_t[:, 0], l_t[:, 1])

        # calc side of square
        side = np.float32(math.sqrt(area) / 2)

        # calc 3 points with unrotated square
        l_t = np.array([g_c + [-side, -side],
                        g_c + [side, -side],
                        g_c + [side, side]])

    # calc affine transform from 3 global space points to 3 local space points size of 'output_size'
    pts2 = np.float32(((0, 0), (output_size, 0), (output_size, output_size)))
    mat = cv2.getAffineTransform(l_t, pts2)
    return mat


def get_rect_from_landmarks(image_landmarks):
    mat = get_transform_mat(image_landmarks, 256, FaceType.FULL_NO_ALIGN)

    g_p = transform_points(np.float32([(0, 0), (255, 255)]), mat, True)

    (l, t, r, b) = g_p[0][0], g_p[0][1], g_p[1][0], g_p[1][1]

    return (l, t, r, b)

def get_transform_mat_all(image_landmarks,uni_landmarks,output_size,scale=1,gcx=-0.02,gcy=0.15,face_type=FaceType.WHOLE_FACE):
    if not isinstance(image_landmarks, np.ndarray):
        image_landmarks = np.array (image_landmarks)
    # estimate landmarks transform from global space to local aligned space with bounds [0..1]

    mat = umeyama(image_landmarks, uni_landmarks, True)[0:2]

    # get corner points in global space
    g_p = transform_points (  np.float32([(0,0),(1,0),(1,1),(0,1),(0.5,0.5) ]) , mat, True)
    g_c = g_p[4]
    # calc diagonal vectors between corners in global space


    tb_diag_vec = (g_p[2] - g_p[0]).astype(np.float32)
    tb_diag_vec /= npla.norm(tb_diag_vec)
    bt_diag_vec = (g_p[1] - g_p[3]).astype(np.float32)
    bt_diag_vec /= npla.norm(bt_diag_vec)

    # calc modifier of diagonal vectors for scale and padding value
    padding, remove_align = FaceType_to_padding_remove_align.get(face_type, 0.0)
    mod = (1.0 / scale) * (npla.norm(g_p[0] - g_p[2]) * (padding * np.sqrt(2.0) + 0.5))

    vec = (g_p[0]-g_p[3]).astype(np.float32)
    vec_len = npla.norm(vec)
    vec /= vec_len
    g_c += vec*vec_len*[gcx,gcy]


    # calc 3 points in global space to estimate 2d affine transform
    if not remove_align:
        l_t = np.array([g_c - tb_diag_vec * mod,
                        g_c + bt_diag_vec * mod,
                        g_c + tb_diag_vec * mod])
    else:
        # remove_align - face will be centered in the frame but not aligned
        l_t = np.array([g_c - tb_diag_vec * mod,
                        g_c + bt_diag_vec * mod,
                        g_c + tb_diag_vec * mod,
                        g_c - bt_diag_vec * mod,
                        ])

        # get area of face square in global space
        area = mathlib.polygon_area(l_t[:, 0], l_t[:, 1])

        # calc side of square
        side = np.float32(math.sqrt(area) / 2)

        # calc 3 points with unrotated square
        l_t = np.array([g_c + [-side, -side],
                        g_c + [side, -side],
                        g_c + [side, side]])

    # calc affine transform from 3 global space points to 3 local space points size of 'output_size'
    pts2 = np.float32(((0, 0), (output_size, 0), (output_size, output_size)))
    mat = cv2.getAffineTransform(l_t, pts2)
    return mat


def expand_eyebrows(lmrks, eyebrows_expand_mod=1.0):

    if len(lmrks) != 68:
        raise Exception('works only with 68 landmarks')
    lmrks = np.array(lmrks.copy(), dtype=np.int)

    # #nose
    ml_pnt = (lmrks[36] + lmrks[0]) // 2
    mr_pnt = (lmrks[16] + lmrks[45]) // 2

    # mid points between the mid points and eye
    ql_pnt = (lmrks[36] + ml_pnt) // 2
    qr_pnt = (lmrks[45] + mr_pnt) // 2

    # Top of the eye arrays
    bot_l = np.array((ql_pnt, lmrks[36], lmrks[37], lmrks[38], lmrks[39]))
    bot_r = np.array((lmrks[42], lmrks[43], lmrks[44], lmrks[45], qr_pnt))

    # Eyebrow arrays
    top_l = lmrks[17:22]
    top_r = lmrks[22:27]

    # Adjust eyebrow arrays
    lmrks[17:22] = top_l + eyebrows_expand_mod * 0.5 * (top_l - bot_l)
    lmrks[22:27] = top_r + eyebrows_expand_mod * 0.5 * (top_r - bot_r)
    return lmrks


def get_image_hull_mask(image_shape, image_landmarks, eyebrows_expand_mod=1.0):
    hull_mask = np.zeros(image_shape[0:2] + (1,), dtype=np.float32)

    lmrks = expand_eyebrows(image_landmarks, eyebrows_expand_mod)

    r_jaw = (lmrks[0:9], lmrks[17:18])
    l_jaw = (lmrks[8:17], lmrks[26:27])
    r_cheek = (lmrks[17:20], lmrks[8:9])
    l_cheek = (lmrks[24:27], lmrks[8:9])
    nose_ridge = (lmrks[19:25], lmrks[8:9],)
    r_eye = (lmrks[17:22], lmrks[27:28], lmrks[31:36], lmrks[8:9])
    l_eye = (lmrks[22:27], lmrks[27:28], lmrks[31:36], lmrks[8:9])
    nose = (lmrks[27:31], lmrks[31:36])
    parts = [r_jaw, l_jaw, r_cheek, l_cheek, nose_ridge, r_eye, l_eye, nose]

    for item in parts:
        merged = np.concatenate(item)
        cv2.fillConvexPoly(hull_mask, cv2.convexHull(merged), (1,))

    return hull_mask


def get_image_eye_mask(image_shape, image_landmarks):
    if len(image_landmarks) != 68:
        raise Exception('get_image_eye_mask works only with 68 landmarks')

    h, w, c = image_shape

    hull_mask = np.zeros((h, w, 1), dtype=np.float32)

    image_landmarks = image_landmarks.astype(np.int)

    cv2.fillConvexPoly(hull_mask, cv2.convexHull(image_landmarks[36:42]), (1,))
    cv2.fillConvexPoly(hull_mask, cv2.convexHull(image_landmarks[42:48]), (1,))

    dilate = h // 32
    hull_mask = cv2.dilate(hull_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (dilate, dilate)), iterations=1)

    blur = h // 16
    blur = blur + (1 - blur % 2)
    hull_mask = cv2.GaussianBlur(hull_mask, (blur, blur), 0)
    hull_mask = hull_mask[..., None]

    return hull_mask


def get_image_mouth_mask(image_shape, image_landmarks):
    if len(image_landmarks) != 68:
        raise Exception('get_image_eye_mask works only with 68 landmarks')

    h, w, c = image_shape

    hull_mask = np.zeros((h, w, 1), dtype=np.float32)

    image_landmarks = image_landmarks.astype(np.int)

    cv2.fillConvexPoly(hull_mask, cv2.convexHull(image_landmarks[60:]), (1,))

    dilate = h // 32
    hull_mask = cv2.dilate(hull_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (dilate, dilate)), iterations=1)

    blur = h // 16
    blur = blur + (1 - blur % 2)
    hull_mask = cv2.GaussianBlur(hull_mask, (blur, blur), 0)
    hull_mask = hull_mask[..., None]

    return hull_mask


def alpha_to_color(img_alpha, color):
    if len(img_alpha.shape) == 2:
        img_alpha = img_alpha[..., None]
    h, w, c = img_alpha.shape
    result = np.zeros((h, w, len(color)), dtype=np.float32)
    result[:, :] = color

    return result * img_alpha


def get_cmask(image_shape, lmrks, eyebrows_expand_mod=1.0):
    h, w, c = image_shape

    hull = get_image_hull_mask(image_shape, lmrks, eyebrows_expand_mod)

    result = np.zeros((h, w, 3), dtype=np.float32)

    def process(w, h, data):
        d = {}
        cur_lc = 0
        all_lines = []
        for s, pts_loop_ar in data:
            lines = []
            for pts, loop in pts_loop_ar:
                pts_len = len(pts)
                lines.append([[pts[i], pts[(i + 1) % pts_len]] for i in range(pts_len - (0 if loop else 1))])
            lines = np.concatenate(lines)

            lc = lines.shape[0]
            all_lines.append(lines)
            d[s] = cur_lc, cur_lc + lc
            cur_lc += lc
        all_lines = np.concatenate(all_lines, 0)

        # calculate signed distance for all points and lines
        line_count = all_lines.shape[0]
        pts_count = w * h

        all_lines = np.repeat(all_lines[None, ...], pts_count, axis=0).reshape((pts_count * line_count, 2, 2))

        pts = np.empty((h, w, line_count, 2), dtype=np.float32)
        pts[..., 1] = np.arange(h)[:, None, None]
        pts[..., 0] = np.arange(w)[:, None]
        pts = pts.reshape((h * w * line_count, -1))

        a = all_lines[:, 0, :]
        b = all_lines[:, 1, :]
        pa = pts - a
        ba = b - a
        ph = np.clip(np.einsum('ij,ij->i', pa, ba) / np.einsum('ij,ij->i', ba, ba), 0, 1)
        dists = npla.norm(pa - ba * ph[..., None], axis=1).reshape((h, w, line_count))

        def get_dists(name, thickness=0):
            s, e = d[name]
            result = dists[..., s:e]
            if thickness != 0:
                result = np.abs(result) - thickness
            return np.min(result, axis=-1)

        return get_dists

    l_eye = lmrks[42:48]
    r_eye = lmrks[36:42]
    l_brow = lmrks[22:27]
    r_brow = lmrks[17:22]
    mouth = lmrks[48:60]

    up_nose = np.concatenate((lmrks[27:31], lmrks[33:34]))
    down_nose = lmrks[31:36]
    nose = np.concatenate((up_nose, down_nose))

    gdf = process(w, h,
                  (
                      ('eyes', ((l_eye, True), (r_eye, True))),
                      ('brows', ((l_brow, False), (r_brow, False))),
                      ('up_nose', ((up_nose, False),)),
                      ('down_nose', ((down_nose, False),)),
                      ('mouth', ((mouth, True),)),
                  )
                  )

    eyes_fall_dist = w // 32
    eyes_thickness = max(w // 64, 1)

    brows_fall_dist = w // 32
    brows_thickness = max(w // 256, 1)

    nose_fall_dist = w / 12
    nose_thickness = max(w // 96, 1)

    mouth_fall_dist = w // 32
    mouth_thickness = max(w // 64, 1)

    eyes_mask = gdf('eyes', eyes_thickness)
    eyes_mask = 1 - np.clip(eyes_mask / eyes_fall_dist, 0, 1)
    # eyes_mask = np.clip ( 1- ( np.sqrt( np.maximum(eyes_mask,0) ) / eyes_fall_dist ), 0, 1)
    # eyes_mask = np.clip ( 1- ( np.cbrt( np.maximum(eyes_mask,0) ) / eyes_fall_dist ), 0, 1)

    brows_mask = gdf('brows', brows_thickness)
    brows_mask = 1 - np.clip(brows_mask / brows_fall_dist, 0, 1)
    # brows_mask = np.clip ( 1- ( np.sqrt( np.maximum(brows_mask,0) ) / brows_fall_dist ), 0, 1)

    mouth_mask = gdf('mouth', mouth_thickness)
    mouth_mask = 1 - np.clip(mouth_mask / mouth_fall_dist, 0, 1)

    # mouth_mask = np.clip ( 1- ( np.sqrt( np.maximum(mouth_mask,0) ) / mouth_fall_dist ), 0, 1)

    def blend(a, b, k):
        x = np.clip(0.5 + 0.5 * (b - a) / k, 0.0, 1.0)
        return (a - b) * x + b - k * x * (1.0 - x)

    # nose_mask = (a-b)*x+b - k*x*(1.0-x)

    # nose_mask = np.minimum (up_nose_mask , down_nose_mask )
    # nose_mask = 1-np.clip( nose_mask / nose_fall_dist, 0, 1)

    nose_mask = blend(gdf('up_nose', nose_thickness), gdf('down_nose', nose_thickness), nose_thickness * 3)
    nose_mask = 1 - np.clip(nose_mask / nose_fall_dist, 0, 1)

    up_nose_mask = gdf('up_nose', nose_thickness)
    up_nose_mask = 1 - np.clip(up_nose_mask / nose_fall_dist, 0, 1)
    # up_nose_mask = np.clip ( 1- ( np.cbrt( np.maximum(up_nose_mask,0) ) / nose_fall_dist ), 0, 1)

    down_nose_mask = gdf('down_nose', nose_thickness)
    down_nose_mask = 1 - np.clip(down_nose_mask / nose_fall_dist, 0, 1)
    # down_nose_mask = np.clip ( 1- ( np.cbrt( np.maximum(down_nose_mask,0) ) / nose_fall_dist ), 0, 1)

    # nose_mask = np.clip( up_nose_mask + down_nose_mask, 0, 1 )
    # nose_mask /= np.max(nose_mask)
    # nose_mask = np.maximum (up_nose_mask , down_nose_mask )
    # nose_mask = down_nose_mask

    # nose_mask = np.zeros_like(nose_mask)

    eyes_mask = eyes_mask * (1 - mouth_mask)
    nose_mask = nose_mask * (1 - eyes_mask)

    hull_mask = hull[..., 0].copy()
    hull_mask = hull_mask * (1 - eyes_mask) * (1 - brows_mask) * (1 - nose_mask) * (1 - mouth_mask)

    # eyes_mask = eyes_mask * (1-nose_mask)

    mouth_mask = mouth_mask * (1 - nose_mask)

    brows_mask = brows_mask * (1 - nose_mask) * (1 - eyes_mask)

    hull_mask = alpha_to_color(hull_mask, (0, 1, 0))
    eyes_mask = alpha_to_color(eyes_mask, (1, 0, 0))
    brows_mask = alpha_to_color(brows_mask, (0, 0, 1))
    nose_mask = alpha_to_color(nose_mask, (0, 1, 1))
    mouth_mask = alpha_to_color(mouth_mask, (0, 0, 1))

    # nose_mask = np.maximum( up_nose_mask, down_nose_mask )

    result = hull_mask + mouth_mask + nose_mask + brows_mask + eyes_mask
    result *= hull
    # result = np.clip (result, 0, 1)
    return result


def blur_image_hull_mask(hull_mask):
    maxregion = np.argwhere(hull_mask == 1.0)
    miny, minx = maxregion.min(axis=0)[:2]
    maxy, maxx = maxregion.max(axis=0)[:2]
    lenx = maxx - minx;
    leny = maxy - miny;
    masky = int(minx + (lenx // 2))
    maskx = int(miny + (leny // 2))
    lowest_len = min(lenx, leny)
    ero = int(lowest_len * 0.085)
    blur = int(lowest_len * 0.10)

    hull_mask = cv2.erode(hull_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (ero, ero)), iterations=1)
    hull_mask = cv2.blur(hull_mask, (blur, blur))
    hull_mask = np.expand_dims(hull_mask, -1)

    return hull_mask


mirror_idxs = [
    [0, 16],
    [1, 15],
    [2, 14],
    [3, 13],
    [4, 12],
    [5, 11],
    [6, 10],
    [7, 9],

    [17, 26],
    [18, 25],
    [19, 24],
    [20, 23],
    [21, 22],

    [36, 45],
    [37, 44],
    [38, 43],
    [39, 42],
    [40, 47],
    [41, 46],

    [31, 35],
    [32, 34],

    [50, 52],
    [49, 53],
    [48, 54],
    [59, 55],
    [58, 56],
    [67, 65],
    [60, 64],
    [61, 63]]


def mirror_landmarks(landmarks, val):
    result = landmarks.copy()

    for idx in mirror_idxs:
        result[idx] = result[idx[::-1]]

    result[:, 0] = val - result[:, 0] - 1
    return result


def get_face_struct_mask(image_shape, image_landmarks, eyebrows_expand_mod=1.0, color=(1,)):
    mask = np.zeros(image_shape[0:2] + (len(color),), dtype=np.float32)
    lmrks = expand_eyebrows(image_landmarks, eyebrows_expand_mod)
    draw_landmarks(mask, image_landmarks, color=color, draw_circles=False, thickness=2)
    return mask


def draw_landmarks(image, image_landmarks, color=(0, 255, 0), draw_circles=True, thickness=1, transparent_mask=False):
    if len(image_landmarks) != 68:
        raise Exception('get_image_eye_mask works only with 68 landmarks')

    int_lmrks = np.array(image_landmarks, dtype=np.int)

    jaw = int_lmrks[slice(*landmarks_68_pt["jaw"])]
    right_eyebrow = int_lmrks[slice(*landmarks_68_pt["right_eyebrow"])]
    left_eyebrow = int_lmrks[slice(*landmarks_68_pt["left_eyebrow"])]
    mouth = int_lmrks[slice(*landmarks_68_pt["mouth"])]
    right_eye = int_lmrks[slice(*landmarks_68_pt["right_eye"])]
    left_eye = int_lmrks[slice(*landmarks_68_pt["left_eye"])]
    nose = int_lmrks[slice(*landmarks_68_pt["nose"])]

    # open shapes
    cv2.polylines(image,
                  tuple(np.array([v]) for v in (right_eyebrow, jaw, left_eyebrow, np.concatenate((nose, [nose[-6]])))),
                  False, color, thickness=thickness, lineType=cv2.LINE_AA)
    # closed shapes
    cv2.polylines(image, tuple(np.array([v]) for v in (right_eye, left_eye, mouth)),
                  True, color, thickness=thickness, lineType=cv2.LINE_AA)

    if draw_circles:
        # the rest of the cicles
        for x, y in np.concatenate((right_eyebrow, left_eyebrow, mouth, right_eye, left_eye, nose), axis=0):
            cv2.circle(image, (x, y), 1, color, 1, lineType=cv2.LINE_AA)
        # jaw big circles
        for x, y in jaw:
            cv2.circle(image, (x, y), 2, color, lineType=cv2.LINE_AA)

    if transparent_mask:
        mask = get_image_hull_mask(image.shape, image_landmarks)
        image[...] = (image * (1 - mask) + image * mask / 2)[...]


def draw_rect_landmarks(image, rect, image_landmarks, face_type, face_size=256, transparent_mask=False,
                        landmarks_color=(0, 255, 0)):
    draw_landmarks(image, image_landmarks, color=landmarks_color, transparent_mask=transparent_mask)
    imagelib.draw_rect(image, rect, (255, 0, 0), 2)

    image_to_face_mat = get_transform_mat(image_landmarks, face_size, face_type)
    points = transform_points([(0, 0), (0, face_size - 1), (face_size - 1, face_size - 1), (face_size - 1, 0)],
                              image_to_face_mat, True)
    imagelib.draw_polygon(image, points, (0, 0, 255), 2)

    points = transform_points(
        [(int(face_size * 0.05), 0), (int(face_size * 0.1), int(face_size * 0.1)), (0, int(face_size * 0.1))],
        image_to_face_mat, True)
    imagelib.draw_polygon(image, points, (0, 0, 255), 2)


def calc_face_pitch(landmarks):
    if not isinstance(landmarks, np.ndarray):
        landmarks = np.array(landmarks)
    t = ((landmarks[6][1] - landmarks[8][1]) + (landmarks[10][1] - landmarks[8][1])) / 2.0
    b = landmarks[8][1]
    return float(b - t)


def estimate_averaged_yaw(landmarks):
    # Works much better than solvePnP if landmarks from "3DFAN"
    if not isinstance(landmarks, np.ndarray):
        landmarks = np.array(landmarks)
    l = ((landmarks[27][0] - landmarks[0][0]) + (landmarks[28][0] - landmarks[1][0]) + (
                landmarks[29][0] - landmarks[2][0])) / 3.0
    r = ((landmarks[16][0] - landmarks[27][0]) + (landmarks[15][0] - landmarks[28][0]) + (
                landmarks[14][0] - landmarks[29][0])) / 3.0
    return float(r - l)


def estimate_pitch_yaw_roll(aligned_landmarks, size=256):
    """
    returns pitch,yaw,roll [-pi/2...+pi/2]
    """
    shape = (size, size)
    focal_length = shape[1]
    camera_center = (shape[1] / 2, shape[0] / 2)
    camera_matrix = np.array(
        [[focal_length, 0, camera_center[0]],
         [0, focal_length, camera_center[1]],
         [0, 0, 1]], dtype=np.float32)

    (_, rotation_vector, _) = cv2.solvePnP(
        np.concatenate((landmarks_68_3D[:27], landmarks_68_3D[30:36]), axis=0),
        np.concatenate((aligned_landmarks[:27], aligned_landmarks[30:36]), axis=0).astype(np.float32),
        camera_matrix,
        np.zeros((4, 1)))

    pitch, yaw, roll = mathlib.rotationMatrixToEulerAngles(cv2.Rodrigues(rotation_vector)[0])

    half_pi = math.pi / 2.0
    pitch = np.clip(pitch, -half_pi, half_pi)
    yaw = np.clip(yaw, -half_pi, half_pi)
    roll = np.clip(roll, -half_pi, half_pi)

    return -pitch, yaw, roll


# if remove_align:
#    bbox = transform_points ( [ (0,0), (0,output_size), (output_size, output_size), (output_size,0) ], mat, True)
#    #import code
#    #code.interact(local=dict(globals(), **locals()))
#    area = mathlib.polygon_area(bbox[:,0], bbox[:,1] )
#    side = math.sqrt(area) / 2
#    center = transform_points ( [(output_size/2,output_size/2)], mat, True)
#    pts1 = np.float32(( center+[-side,-side], center+[side,-side], center+[side,-side] ))
#    pts2 = np.float32([[0,0],[output_size,0],[0,output_size]])
#    mat = cv2.getAffineTransform(pts1,pts2)
# if full_face_align_top and (face_type == FaceType.FULL or face_type == FaceType.FULL_NO_ALIGN):
#    #lmrks2 = expand_eyebrows(image_landmarks)
#    #lmrks2_ = transform_points( [ lmrks2[19], lmrks2[24] ], mat, False )
#    #y_diff = np.float32( (0,np.min(lmrks2_[:,1])) )
#    #y_diff = transform_points( [ np.float32( (0,0) ), y_diff], mat, True)
#    #y_diff = y_diff[1]-y_diff[0]
#
#    x_diff = np.float32((0,0))
#
#    lmrks2_ = transform_points( [ image_landmarks[0], image_landmarks[16] ], mat, False )
#    if lmrks2_[0,0] < 0:
#        x_diff = lmrks2_[0,0]
#        x_diff = transform_points( [ np.float32( (0,0) ), np.float32((x_diff,0)) ], mat, True)
#        x_diff = x_diff[1]-x_diff[0]
#    elif lmrks2_[1,0] >= output_size:
#        x_diff = lmrks2_[1,0]-(output_size-1)
#        x_diff = transform_points( [ np.float32( (0,0) ), np.float32((x_diff,0)) ], mat, True)
#        x_diff = x_diff[1]-x_diff[0]
#
#    mat = cv2.getAffineTransform( l_t+y_diff+x_diff ,pts2)


"""
def get_averaged_transform_mat (img_landmarks,
                                img_landmarks_prev,
                                img_landmarks_next,
                                average_frame_count,
                                average_center_frame_count,
                                output_size, face_type, scale=1.0):

    l_c_list = []
    tb_diag_vec_list = []
    bt_diag_vec_list = []
    mod_list = []

    count = max(average_frame_count,average_center_frame_count)
    for i in range ( -count, count+1, 1 ):
        if i < 0:
            lmrks = img_landmarks_prev[i] if -i < len(img_landmarks_prev) else None
        elif i > 0:
            lmrks = img_landmarks_next[i] if i < len(img_landmarks_next) else None
        else:
            lmrks = img_landmarks

        if lmrks is None:
            continue

        l_c, tb_diag_vec, bt_diag_vec, mod = get_transform_mat_data (lmrks, face_type, scale=scale)

        if i >= -average_frame_count and i <= average_frame_count:
            tb_diag_vec_list.append(tb_diag_vec)
            bt_diag_vec_list.append(bt_diag_vec)
            mod_list.append(mod)

        if i >= -average_center_frame_count and i <= average_center_frame_count:
            l_c_list.append(l_c)

    tb_diag_vec = np.mean( np.array(tb_diag_vec_list), axis=0 )
    bt_diag_vec = np.mean( np.array(bt_diag_vec_list), axis=0 )
    mod         = np.mean( np.array(mod_list), axis=0 )
    l_c         = np.mean( np.array(l_c_list), axis=0 )

    return get_transform_mat_by_data (l_c, tb_diag_vec, bt_diag_vec, mod, output_size, face_type)


def get_transform_mat (image_landmarks, output_size, face_type, scale=1.0):
    if not isinstance(image_landmarks, np.ndarray):
        image_landmarks = np.array (image_landmarks)

    # get face padding value for FaceType
    padding, remove_align = FaceType_to_padding_remove_align.get(face_type, 0.0)

    # estimate landmarks transform from global space to local aligned space with bounds [0..1]
    mat = umeyama( np.concatenate ( [ image_landmarks[17:49] , image_landmarks[54:55] ] ) , landmarks_2D_new, True)[0:2]

    # get corner points in global space
    l_p = transform_points (  np.float32([(0,0),(1,0),(1,1),(0,1),(0.5,0.5)]) , mat, True)
    l_c = l_p[4]

    # calc diagonal vectors between corners in global space
    tb_diag_vec = (l_p[2]-l_p[0]).astype(np.float32)
    tb_diag_vec /= npla.norm(tb_diag_vec)
    bt_diag_vec = (l_p[1]-l_p[3]).astype(np.float32)
    bt_diag_vec /= npla.norm(bt_diag_vec)

    # calc modifier of diagonal vectors for scale and padding value
    mod = (1.0 / scale)* ( npla.norm(l_p[0]-l_p[2])*(padding*np.sqrt(2.0) + 0.5) )

    # calc 3 points in global space to estimate 2d affine transform
    if not remove_align:
        l_t = np.array( [ np.round( l_c - tb_diag_vec*mod ),
                          np.round( l_c + bt_diag_vec*mod ),
                          np.round( l_c + tb_diag_vec*mod ) ] )
    else:
        # remove_align - face will be centered in the frame but not aligned
        l_t = np.array( [ np.round( l_c - tb_diag_vec*mod ),
                          np.round( l_c + bt_diag_vec*mod ),
                          np.round( l_c + tb_diag_vec*mod ),
                          np.round( l_c - bt_diag_vec*mod ),
                         ] )

        # get area of face square in global space
        area = mathlib.polygon_area(l_t[:,0], l_t[:,1] )

        # calc side of square
        side = np.float32(math.sqrt(area) / 2)

        # calc 3 points with unrotated square
        l_t = np.array( [ np.round( l_c + [-side,-side] ),
                          np.round( l_c + [ side,-side] ),
                          np.round( l_c + [ side, side] ) ] )

    # calc affine transform from 3 global space points to 3 local space points size of 'output_size'
    pts2 = np.float32(( (0,0),(output_size,0),(output_size,output_size) ))
    mat = cv2.getAffineTransform(l_t,pts2)

    return mat
"""
